Начиная
#######

Genode можно рассматривать с двух разных точек зрения: как архитектуру операционной системы или как практический набор инструментов. Эта глава поможет вам изучить Genode как последнее. После знакомства с рекомендуемой средой разработки она проведёт вас через шаги, необходимые для получения исходного кода (раздел [Получение исходного кода]), для использования цепочки инструментов (раздел [Использование системы сборки]), для тестирования системных сценариев (раздел [Простой системный сценарий]) и создания своего первого пользовательского компонента с нуля (раздел [Hello world]).


Рекомендуемая среда разработки
------------------------------

Genode регулярно используется и разрабатывается в GNU/Linux. Рекомендуется использовать последнюю версию Ubuntu с долгосрочной поддержкой (LTS). Убедитесь, что ваша установка удовлетворяет следующим требованиям:

* GNU Make версии 3.81 (или новее), необходимой для системы сборки,
* _libSDL-dev_, необходимый для запуска системных сценариев непосредственно в Linux,
* _tclsh_ и _expect_, необходимые для автоматизации тестирования и инструментов рабочего процесса,
* _xmlint_ для проверки конфигураций,
* _qemu_, _xorriso_, _sgdisk_ и _e2tools_, необходимые для запуска системных сценариев на платформах, отличных от Linux, через эмулятор Qemu.
      
Для использования всей коллекции портированного стороннего ПО необходимо дополнительно установить следующие пакеты: _byacc_, _autoconf2.64_, _autogen_, _bison_, _flex_, _g++_, _git_, _gperf_, _libxml2-utils_, _subversion_ и _xsltproc_.


Поиск помощи
------------

Лучший способ получить помощь при изучении Genode — обратиться к списку рассылки, который является основным средством общения как обычных пользователей, так и разработчиков. Пожалуйста, не стесняйтесь присоединиться!

:Списки рассылки:

  [https://genode.org/community/mailing-lists]

Если вы столкнулись с новой ошибкой, неоднозначной документацией или отсутствующей функцией, рассмотрите возможность создания соответствующей проблемы в системе отслеживания проблем:

:Трекер проблем:

  [https://github.com/genodelabs/genode/issues]


Получение исходного кода
========================

Центральным элементом Genode является исходный код, найденный в официальном репозитории Git:

:Исходный код на GitHub:

  [https://github.com/genodelabs/genode]

Чтобы получить исходный код, клонируйте репозиторий Git:

! git clone https://github.com/genodelabs/genode.git

После клонирования вы можете найти исходный код в каталоге _genode/_. Далее мы будем ссылаться на этот каталог как _<genode-dir>_.

Git проверяет самую последнюю фиксацию главной ветви Genode, поэтому давайте теперь перейдём к версии, используемой в этом руководстве:

! cd <genode-dir>
! git checkout -b 22.05 22.05


Древовидная структура исходного кода
====================================

Каталог верхнего уровня
-----------------------

В корне дерева каталогов находится следующее содержимое:

:_doc/_: Документация в обычном текстовом формате, включая примечания к выпуску всех версий. _:Совет:_ Подробные примечания к выпуску содержат большую часть практической документации, собранной за время существования проекта. Когда вам интересна определённая тема, часто стоит применить grep с ней к примечаниям к выпуску, чтобы получить отправную точку для исследования.

:_tool/_: Инструменты и скрипты для поддержки системы сборки, различных загрузчиков, набора инструментов и управления исходным кодом сторонних производителей. Пожалуйста, найдите более подробную информацию в файле _README_, содержащемся в подкаталоге.

:_repos/_: Так называемые репозитории исходного кода, которые содержат фактический исходный код компонентов фреймворка. Исходный код организован в пределах не одного исходного дерева, а нескольких деревьев. Каждое дерево называется _source-code repository_ и имеет одинаковую принципиальную структуру. Во время сборки может быть выбран набор репозиториев исходного кода для включения в процесс сборки. Таким образом, репозитории исходного кода обеспечивают крупномасштабную модульность фреймворка.


Обзор репозиториев
------------------

Каталог _<genode-dir>/repos/_ содержит следующие репозитории исходного кода.

:_base/_:

  Основные интерфейсы фреймворка, а также независимые от платформы части основного компонента (раздел [Ядро — корень дерева компонентов]).

:_base-<platform>/_:
  Дополнения к _base/_ репозиторию, зависящие от конкретной платформы, где _<platform>_ соответствует одному из следующих:

  :_linux_:

    Ядро Linux (как x86_32, так и x86_64).

  :_nova_:

     Микрогипервизор NOVA. Более подробная информация о платформе NOVA представлена в разделе [Выполнение на микрогипервизоре NOVA (base-nova)].

  :_hw_:

    Платформа hw позволяет выполнять Genode на голом оборудовании без необходимости в отдельном ядре. Функциональность ядра включена в основной компонент. Он поддерживает 32-разрядную ARM, 64-разрядную ARM, 64-разрядные архитектуры процессоров x86 и 64-разрядные архитектуры процессоров RISC-V. Более подробную информацию о платформе hw можно найти в разделе [Выполнение на голом оборудовании (base-hw)].

  :_sel4_:

    Микроядро seL4 сочетает в себе философию ядра L4 с формальной верификацией. Поддержка этого ядра является экспериментальной.

  :_foc_:

    Fiasco.OC — это модернизированная версия микроядра L4/Fiasco с полностью переработанным интерфейсом ядра, обеспечивающим безопасность на основе возможностей.

  :_okl4_:

    Ядро OKL4 первоначально разработано в Open-Kernel-Labs.

  :_pistachio_:

   Ядро L4ka::Pistachio разработано в Университете Карлсруэ.

  :_fiasco_:

    Ядро L4/Fiasco первоначально было разработано в Техническом университете Дрездена.

:_os/_:

  Компоненты ОС, такие как компонент инициализации, драйверы устройств и базовые системные службы.

:_demo/_:

  Различные сервисы и приложения, используемые в демонстрационных целях, например, программа запуска графических приложений и обучающий браузер, описанные в разделе [Простой системный сценарий], можно найти здесь.

:_hello_tutorial/_:

  Руководство по созданию простого сценария клиент-сервер. Этот репозиторий включает документацию и полный исходный код.

:_libports/_:

  Порты популярных библиотек с открытым исходным кодом, наиболее важной из которых является библиотека C. Среди сторонних библиотек — стандартная библиотека C++, Qt5, FreeType, ncurses, libUSB, curl, lwip и Mesa.

:_dde_linux/_:

  Среда драйвера устройства для выполнения подсистем ядра Linux в качестве компонентов пользовательского уровня. Помимо размещения портов универсальных подсистем ядра Linux, таких как стек TCP/IP, он является основой для многих размещённых в других репозиториях драйверов, специфичных для плат.

:_dde_ipxe/_:

  Среда драйверов устройств для выполнения сетевых драйверов проекта iPXE.

:_dde_bsd/_:

  Среда драйверов устройств для аудиодрайверов, портированная с OpenBSD.

:_dde_rump/_:

  Порт ядер rump, которые используются для выполнения подсистем ядра NetBSD в качестве компонентов пользовательского уровня. Репозиторий содержит сервер, который использует ядро rump для предоставления различных файловых систем NetBSD.

:_pc/_:

  Драйверы устройств для аппаратного обеспечения x86 PC. Репозиторий _pc_ зависит от репозитория _dde_linux, поскольку такие драйверы, как USB-стек, беспроводной стек Intel или графический драйвер Intel, основаны на инфраструктуре _dde_linux/_.

:_ports/_:

Порты сторонних приложений.

:_gems/_:

  Компоненты, которые используют как собственные интерфейсы Genode, так и функции других репозиториев высокого уровня, в частности разделяемые библиотеки, предоставляемые _libports/_.

В дополнение к репозиториям, размещённым в основном дереве исходных текстов Genode, существует ряд внешних репозиториев, которые расширяют фреймворк дополнительными функциями, такими как дополнительные компоненты и поддержка плат различных аппаратных платформ.

:Дополнительные репозитории, поддерживаемые Genode Labs:

  [https://github.com/orgs/genodelabs/repositories]


Использование системы сборки
============================

Genode использует пользовательский набор инструментов, который можно загрузить на следующем
веб-сайте:

:Цепочка инструментов:

  [https://genode.org/download/tool-chain]


Каталог сборки
--------------

Система сборки никогда не обращается к дереву исходных текстов, но генерирует объектные файлы, библиотеки и программы в выделенном каталоге сборки. У нас ещё нет каталога сборки. Для быстрого начала давайте создадим его, используя следующую команду:

! cd <genode-dir>
! ./tool/create_builddir x86_64

_ Чтобы выполнить последующие шаги по тестированию Linux-версии Genode, указанный аргумент платформы должен соответствовать установке вашей хост-ОС. Если вы используете 32-разрядную установку, укажите 'x86_32' вместо 'x86_64'._

Команда создает новый каталог сборки по адресу 'build/x86_64'.


Конфигурация сборки
-------------------

Перед использованием каталога сборки рекомендуется повторно просмотреть и, возможно, скорректировать конфигурацию сборки, которая находится в подкаталоге _etc/_ каталога сборки, например, _build/x86_64/etc/_. Файл _build.conf_ содержит глобальные параметры сборки, в частности выбор репозиториев исходного кода для включения, используемое ядро ('KERNEL') и целевую плату ('BOARD'). Это также подходящее место для добавления глобальных параметров сборки. Например, чтобы разрешить GNU make использовать 4 ядра процессора, используйте следующую строку в файле _build.conf_:

! MAKE += -j4

Вы также можете рассмотреть возможность ускорения вашего рабочего процесса, разрешив использование кэша компилятора ('ccache'), используя следующую строку:

! CCACHE := yes


Сборка компонентов
------------------

Инструкция для создания компонента имеет форму файла _target.mk_ в каталоге _src/_ одного из репозиториев исходного кода. Например, файл _target.mk_ компонента init находится по адресу _<genode-dir>/repos/os/src/init/target.mk_. Чтобы создать компонент, выполните следующую команду из каталога сборки:

! make init

Аргумент "init" ссылается на путь относительно подкаталога _src/_. Система сборки определяет и создаёт все целевые объекты, найденные по этому пути, во всех репозиториях исходного кода. Когда сборка завершена, результирующий исполняемый двоичный файл можно найти в подкаталоге, который соответствует пути цели. Кроме того, система сборки устанавливает символическую ссылку в подкаталог _bin/_, которая указывает на исполняемый двоичный файл.

Если указанный путь содержит несколько файлов _target.mk_ в разных подкаталогах, система сборки создаёт их все. Например, следующая команда создаёт все целевые объекты, найденные в одном из подкаталогов _<repo>/src/drivers/_:

! make drivers

Кроме того, можно указать сразу несколько целей. Следующая команда создаёт как компонент инициализации, так и серверный компонент nitpicker GUI:

! make init server/nitpicker

Простой системный сценарий
===========================

Каталог сборки предлагает гораздо больше, чем просто среду для сборки компонентов. Он поддерживает автоматизацию рабочих процессов системной интеграции, которые обычно включают следующие шаги:

# Создание набора компонентов,
# Настройка статической части системного сценария,
# Сборка загрузочного каталога со всеми частями, необходимыми для сценария,
# Создание загрузочного образа, который можно загрузить на целевую платформу,
# Загрузка целевой платформы с помощью загрузочного образа,
# Проверка поведения сценария.

Рецепт такой последовательности шагов может быть выражен в виде так называемого сценария запуска. Каждый сценарий запуска представляет собой системный сценарий и содержит всю информацию, необходимую для воспроизведения сценария. Запускаемые скрипты могут находиться в подкаталоге _run/_ любого репозитория исходного кода.

Genode поставляется с готовым к использованию сценарием запуска, демонстрирующим простой графический демонстрационный сценарий. Сценарий запуска находится по адресу _<genode-dir>/repos/os/run/demo.run_. Он использует инструменты управления пакетами Genode для сборки статического образа системы. Управление пакетами подробно описано в разделе [Управление пакетами]. На данный момент мы можем пропустить подробности, проинструктировав систему сборки автоматически создавать пакеты и обновлять их номера версий для нас. Раскомментируйте следующую строку в файле _build/x86_64/build.conf_:

! RUN_OPT += --depot-auto-update

В отличие от сборки отдельных компонентов, как описано в предыдущем разделе, интеграция полного системного сценария требует от нас выбора конкретного ядра ОС для использования. Следующая команда инструктирует систему сборки создать, интегрировать и запустить сценарий "run/demo" на ядре Linux:

! make run/demo KERNEL=linux BOARD=linux

Команда запрашивает систему сборки для поиска сценария запуска с именем _demo.run_ во всех репозиториях, перечисленных в _etc/build.conf_. В конечном итоге он найдет сценарий запуска в репозитории _os/_. После завершения сборки всех необходимых компонентов команда автоматически запустит сценарий. Поскольку каталог сборки был создан для платформы _x86_64_, и мы указали "linux" в качестве "KERNEL", сценарий будет выполняться непосредственно в хост-системе, где каждый компонент Genode находится в отдельном процессе Linux. Чтобы изучить сценарий, следуйте инструкциям, предоставленным браузером графического руководства.

Терминал, на котором была выдана команда 'make run/demo', отображает выходные данные журнала системы Genode. Чтобы отменить выполнение, нажмите _control-c_ в терминале.


Ориентация на микроядро
-----------------------

В то время как возможность запускать системные сценарии поверх Linux обеспечивает удобную и быструю разработку компонентов и протоколов, Genode в первую очередь предназначен для использования микроядер. Выбор микроядра для использования зависит от пользователя платформы и может зависеть от различных факторов, таких как набор функций, поддерживаемые аппаратные архитектуры, лицензия или сообщество разработчиков. Чтобы выполнить демонстрационный сценарий непосредственно на микрогипервизоре NOVA, необходимы следующие подготовительные шаги:

# Загрузите сторонний исходный код микрогипервизора NOVA
  ! <genode-dir>/tool/ports/prepare_port nova

Инструмент _prepare_port_ загружает исходный код NOVA в подкаталог по адресу _<genode-dir>/contrib/nova-<hash>/_, где _<hash>_ однозначно относится к подготовленной версии NOVA.

# На реальном оборудовании сценарию требуется драйвер фреймбуфера. Драйвер VESA использует стороннюю библиотеку эмуляции x86 для выполнения кода VESA BIOS. Загрузите сторонний исходный код библиотеки _x86emu_:
 ! <genode-dir>/tool/ports/prepare_port x86emu

Исходный код будет загружен в _<genode-dir>/contrib/x86emu-<hash>/_.

# Для работы с USB-устройствами используется среда драйверов устройств для выполнения подсистемы ядра Linux в качестве компонента пользовательского уровня. Загрузите сторонний исходный код репозитория _dde_linux_:
  ! <genode-dir>/tool/ports/prepare_port dde_linux linux

Исходный код будет загружен в
  _<genode-dir>/contrib/dde_linux-<hash>/_ и
  _<genode-dir>/contrib/linux-<hash>/_ соответственно.

# Для загрузки сценария в качестве операционной системы на ПК необходим загрузчик. В процессе сборки создается загрузочный диск или ISO-образ, который включает загрузчик GRUB2, а также рабочую конфигурацию загрузчика. Загрузите загрузчик в качестве компонента для этапа создания образа.

  ! <genode-dir>/tool/ports/prepare_port grub2

# Поскольку NOVA поддерживает архитектуру x86_64 нашего каталога сборки, мы можем продолжать использовать существующий каталог сборки, который мы только что использовали для Linux. Однако, помимо включения распараллеливания процесса сборки, как упомянуто в разделе [Использование системы сборки], нам необходимо включить репозитории исходного кода _libports_, _dde_linux_ и _pc_ в процесс сборки, раскомментировав соответствующие строки в конфигурации. В противном случае системе сборки не удастся создать драйверы VESA и USB HID, которые находятся в _libports/_, _dde_linux/_ и _pc/_ соответственно.

Выполнив эти приготовления, запустите сценарий демонстрационного запуска из каталога сборки:

! make run/demo KERNEL=nova BOARD=pc

На этот раз будет запущен экземпляр Qemu для выполнения демонстрационного сценария. Аргументы командной строки Qemu отображаются в выходных данных журнала. Как следует из аргументов, сценарий передается в Qemu в виде ISO-образа, находящегося в _var/run/demo.iso_. Этот ISO-образ можно использовать не только с Qemu, но и с реальной машиной. Например, создать загрузочный USB-накопитель с помощью системного сценария так же просто, как записать ISO-образ на USB-накопитель:
! sudo dd if=var/run/demo.iso of=/dev/<usb-device> bs=8M conv=fsync

Обратите внимание, что _<usb-device>_ ссылается на USB-накопитель. Его можно определить с помощью команды 'dmesg' после подключения USB-накопителя. Для загрузки с USB-накопителя вам может потребоваться соответствующим образом настроить установки BIOS тестируемой машины.


Привет, мир
===========

В этом разделе представлены шаги, необходимые для создания и выполнения простого пользовательского компонента, который печатает сообщение hello-world.


Использование пользовательского хранилища исходного кода
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

В принципе, можно было бы добавить новый компонент в одно из существующих хранилищ исходного кода, найденных по адресу _<genode-dir>/repos/_. Однако, если компонент не предназначен для включения в последующую разработку проекта Genome, обычно рекомендуется хранить пользовательский код отдельно от кодовой базы Genode. Это облегчает будущие обновления до новых версий Genode и позволяет вам выбрать систему контроля версий по вашему выбору.

Новый репозиторий должен появиться в каталоге _<genode-dir>/repos/_. Этого можно достичь, либо разместив его в виде подкаталога, либо создав символическую ссылку, указывающую на произвольное местоположение по вашему выбору. А пока давайте разместим новый репозиторий исходного кода под названием "lab" непосредственно в каталоге _repos/_.

! cd <genode-dir>
! mkdir repos/lab

Лабораторный репозиторий будет содержать исходный код и правила сборки для одного компонента, а также сценарий запуска для выполнения компонента в Genode. Исходный код компонента находится в подкаталоге _src/_. По соглашению, каталог _src/_ содержит дополнительные подкаталоги для размещения различных типов компонентов, в частности _server_ (службы и стеки протоколов), _drivers_ (драйверы аппаратных устройств) и _app_ (приложения). Для компонента hello-world подходящим местоположением будет _src/app/hello/_:

! mkdir -p repos/lab/src/app/hello


Исходный код и описание сборки
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Каталог _hello/_ содержит как исходный код, так и описание сборки компонента. Основная часть каждого компонента обычно находится в файле с именем _main.cc_. Следовательно, для программы hello-world мы должны создать файл _repos/lab/src/app/hello/main.cc_ со следующим содержимым:

! #include <base/component.h>
! #include <base/log.h>
!
! void Component::construct(Genode::Env &)
! {
!   Genode::log("Hello world");
! }

Заголовок _base/component.h_ содержит интерфейс, который должен реализовать каждый компонент. Функция 'construct' вызывается средой выполнения компонента для инициализации компонента. Интерфейс к среде выполнения передаётся в качестве аргумента. Этот интерфейс позволяет коду приложения взаимодействовать с внешним миром. Приведённый выше простой пример просто выдаёт сообщение журнала. Функция 'log' определена в заголовке _base/log.h_.

Компонент не существует после возврата функции 'construct'. Вместо этого он становится готовым отвечать на запросы или сигналы, исходящие от других компонентов. Однако приведённый выше пример не взаимодействует с другими компонентами. Следовательно, он просто будет продолжать ждать бесконечно.

Пожалуйста, обратите внимание, что существует рекомендуемый стиль программирования для существующих компонентов Genode. Если вы рассматриваете возможность отправки своей работы для последующей разработки проекта, пожалуйста, обратите внимание на эти общие рекомендации.

:Coding-style guidelines:

  https://genode.org/documentation/developer-resources/coding_style

Исходный файл _main.cc_ сопровождается файлом описания сборки под названием _target.mk_. Он содержит объявления для исходных файлов, библиотеки, используемые компонентом, и имя компонента. Создайте файл _repos/lab/src/app/hello/target.mk_ со следующим содержимым:

! TARGET = hello
! SRC_CC = main.cc
! LIBS  += base


Сборка компонента
~~~~~~~~~~~~~~~~~

Теперь, когда файл описания сборки на месте, пришло время создать новый компонент, например, из каталога сборки _x86_64_, созданного в разделе [Простой системный сценарий]. Чтобы помочь системе сборки найти компонент, мы должны расширить конфигурацию сборки _<build-dir>/etc/build.conf_, добавив следующую строку:

! REPOSITORIES += $(GENODE_DIR)/repos/lab

При добавлении этой строки, система сборки будет учитывать наш собственный репозиторий исходного кода. Чтобы создать компонент, выполните следующую команду:

! make app/hello

На этом шаге компилируется файл _main.cc_ и линкуется исполняемый двоичный файл ELF под названием "hello". Результат можно найти в подкаталоге _<build-dir>/app/hello/_.

Определение системного сценария
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Для тестирования компонента нам необходимо определить системный сценарий, который включает этот компонент. Как упоминалось в разделе [Простой системный сценарий], такое описание имеет форму сценария запуска. Чтобы оснастить репозиторий _lab_ сценарием запуска, нам сначала нужно создать подкаталог _lab/run/_:

! mkdir <genode-dir>/repos/lab/run

В этом каталоге мы создаём файл _<genode-dir>/repos/lab/run/hello.run_ со следующим содержимым:

!build "core init app/hello"
!create_boot_directory
!install_config {
! <config>
!   <parent-provides>
!     <service name="LOG"/>
!     <service name="PD"/>
!     <service name="CPU"/>
!     <service name="ROM"/>
!   </parent-provides>
!   <default-route>
!     <any-service> <parent/> </any-service>
!   </default-route>
!   <default caps="100"/>
!   <start name="hello">
!     <resource name="RAM" quantum="10M"/>
!   </start>
! </config>
!}
!build_boot_image "core ld.lib.so init hello"
!append qemu_args "-nographic -m 64"
!run_genode_until {Hello world.*\n} 10

Этот сценарий запуска выполняет следующие шаги:

# Он создаёт компоненты core, init и app/hello.
; XXX ссылки на разделы о core и инициализации

# Он создаёт новый загрузочный каталог в _<build-dir>/var/run/hello_. Этот каталог содержит все файлы, которые окажутся в конечном загрузочном образе.

# Он создаёт конфигурацию для компонента инициализации. Конфигурация запускает компонент hello как единственный дочерний элемент init. Запросы сеанса, исходящие от компонента hello, всегда будут направлены к родительскому элементу init, который является основным. Узел "<default>" объявляет, что каждый компонент может использовать до 100 возможностей.

# Он собирает загрузочный образ с исполняемым ядром двоичных файлов ELF, ld.lib.so (динамический компоновщик), init и hello. Двоичные файлы извлекаются из подкаталога _<build-dir>/bin/_.

# Он инструктирует Qemu (если используется) отключить графический вывод.

# Он запускает выполнение системного сценария и просматривает выходные данные журнала для данного регулярного выражения. Выполнение завершается, когда появляется вывод журнала или по истечении тайм-аута в 10 секунд.

Этот сценарий запуска выполняет следующие шаги:

Сценарий запуска может быть выполнен из каталога сборки с помощью команды:

! make run/hello KERNEL=linux BOARD=linux

После вывода загрузки используемого ядра сценарий выдаст следующий вывод:

! [init -> hello] Hello world
!
! Run script execution successful.

Метка в квадратных скобках в начале каждой строки идентифицирует компонент, из которого исходило сообщение. Последняя строка выводится инструментом запуска после того, как он успешно сопоставил выходные данные журнала с регулярным выражением, указанным в команде 'run_genode_until'.


[raw spec/clearpage]

Реагирование на внешние события
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Обычно большинство компонентов реагируют на внешние события, такие как пользовательский ввод, события таймера, прерывания устройства, поступление новых данных или RPC-запросы, выданные другими компонентами.

В следующем примере представлен типичный каркас такого компонента. Функция 'construct' просто создаёт объект, представляющий приложение в виде 'статической' локальной переменной. Фактический код компонента находится внутри класса 'Main'.

! #include <base/component.h>
! #include <base/log.h>
! #include <timer_session/connection.h>
!
! namespace Hello { struct Main; }
!
! struct Hello::Main
! {
!   Genode::Env &_env;
!
!   Timer::Connection _timer { _env };
!
!   void _handle_timeout()
!   {
!     Genode::log("woke up at ", _timer.elapsed_ms(), " ms");
!   }
!
!   Genode::Signal_handler<Main> _timeout_handler {
!     _env.ep(), *this, &Main::_handle_timeout };
!
!   Main(Genode::Env &env) : _env(env)
!   {
!     _timer.sigh(_timeout_handler);
!     _timer.trigger_periodic(1000*1000);
!     Genode::log("component constructed");
!   }
! };
!
! void Component::construct(Genode::Env &env)
! {
!   static Hello::Main main(env);
! }

Во-первых, обратите внимание на пространство имён 'Hello'. В качестве хорошей практики код компонента обычно находится в пространстве имён. Пространство имён, специфичное для компонента, может включать в себя другие пространства имён -- в частности, пространство имён 'Genode' -- без загрязнения глобальной области.

Конструктор объекта 'Main' принимает среду Genode в качестве аргумента и сохраняет её в качестве ссылочной переменной-члена '_env'. Переменная-член имеет префикс подчёркивания, чтобы подчеркнуть тот факт, что она является частной для класса 'Main'. В принципе, "Main" может быть "классом", а "_env" является частью "частного" сектора, но поскольку "Main" - это класс верхнего уровня компонента, к которому не обращаются никакие другие части программы, мы используем "struct" для краткости, в то время как по-прежнему сохраняется соглашение о префиксе частных членов символом подчеркивания. Обнаруживая использование такой переменной с префиксом в коде, мы сразу видим, что она является частью объектного контекста кода, а не аргументом или локальной переменной.

Объединяя 'Timer::Connection' в качестве переменной-члена, объект 'Main' запрашивает сеанс у службы таймера во время построения. Поскольку этот запрос сеанса требует взаимодействия с внешним миром, '_env' необходимо передать конструктору '_timer'.

Для того, чтобы реагировать на события от таймера, класс 'Main' содержит объект '_timeout_handler'. Его аргументы конструктора ссылаются на объект и метод, которые должны выполняться всякий раз, когда происходит событие. Объект обработчика тайм-аута регистрируется в '_timer' как получатель событий тайм-аута с помощью метода 'sigh'. Наконец, таймеру даётся указание запускать события тайм-аута со скоростью 1 секунда.

Следует отметить следующие замечания:

* Стиль программирования выражает то, чем является компонент, а не то, что компонент делает.

* Компонент не выполняет никакого динамического выделения памяти.

* При вызове метод '_handle_timeout' имеет свой контекст (объект 'Main'), который легко доступен, что делает применение изменений внутреннего состояния в качестве реакции на внешние события очень естественным.

* Ни функция 'construct', ни метод 'Main::_handle_timeout' не блокируют внешние события.

* Компонент не получает никаких указаний о количестве произошедших событий, только тот факт, что произошло хотя бы одно событие. Код '_handle_timeout' явно запрашивает текущее время у драйвера таймера через синхронный RPC-вызов 'elapsed_ms'.

Чтобы запустить новую версию компонента, нам нужно немного изменить сценарий запуска.

!build "core init timer app/hello"
!create_boot_directory
!install_config {
! <config>
!   <parent-provides>
!     <service name="LOG"/>
!     <service name="PD"/>
!     <service name="CPU"/>
!     <service name="ROM"/>
!   </parent-provides>
!   <default-route>
!     <any-service> <parent/> <any-child/> </any-service>
!   </default-route>
!   <default caps="100"/>
!   <start name="timer">
!     <resource name="RAM" quantum="1M"/>
!     <provides> <service name="Timer"/> </provides>
!   </start>
!   <start name="hello">
!     <resource name="RAM" quantum="10M"/>
!   </start>
! </config>
!}
!build_boot_image "core ld.lib.so init timer hello"
!append qemu_args "-nographic -m 64"
!run_genode_until forever

Изменения заключаются в следующем:

* Поскольку компонент hello теперь полагается на службу таймера, нам необходимо создать и интегрировать драйвер таймера в сценарий, соответственно расширив шаги 'build' и 'build_boot_image'.

* Мы даём команду init запустить драйвер таймера в качестве дополнительного компонента, добавив узел '<start>' в его конфигурацию. Внутри этого узла мы объявляем, что компонент предоставляет услугу типа "Таймер".

* Чтобы позволить компоненту hello открывать сеанс "таймера" в драйвере таймера, маршрут по умолчанию изменён, чтобы рассматривать любые дочерние элементы в качестве серверов всякий раз, когда запрашиваемая услуга не предоставляется родительским.

* На этот раз мы позволяем сценарию выполняться вечно, чтобы мы могли просматривать сообщения, печатаемые с периодическими интервалами.

При запуске сценария запуска мы можем наблюдать периодическую активацию компонента в выводе журнала:

![init] child "timer" announces service "Timer"
![init -> hello] component constructed
![init -> hello] woke up at 12 ms
![init -> hello] woke up at 1008 ms
![init -> hello] woke up at 2005 ms
!...


Следующие шаги
==============

Есть несколько возможных способов продолжить ваше исследование.

# Чтобы сформировать *ментальную модель* того, как работает Genode, прочтите главу [Архитектура]. В частности, вашего внимания заслуживают разделы [Безопасность на основе возможностей], [Рекурсивная структура системы], [Торговля ресурсами] и [Межкомпонентная коммуникация]. Раздел [Состав компонентов] прекрасно дополняет этот теоретический материал рядом наглядных примеров.

# Если вам не терпится увидеть, как раскрывается потенциал Genode, попробуйте основанную на Genode операционную систему Sculpt OS, которая доступна в виде загружаемого образа системы вместе с подробной документацией.

  :Sculpt OS:

    [https://genode.org/download/sculpt]

# Genode включает в себя *набор функций* из сотен готовых к использованию компонентов, которые интересно открывать и комбинировать. Большинство компонентов не только сопровождаются документацией в виде файлов README, локальных для соответствующего исходного кода, но и поставляются по крайней мере с одним готовым к использованию сценарием запуска или тестовым пакетом, который иллюстрирует интеграцию компонента в исполняемую форму. Ознакомившись с концепцией запуска скриптов, прочитав раздел [Системная интеграция и автоматизированное тестирование], просмотрите подкаталоги _run/_ и _recipes/pkg/_ различных репозиториев исходного кода, например, в репозиториях/os/run/_ или _repos/gems/run/_.

# Чтобы следить за работой *сообщества* Genode, узнать о текущих направлениях работы, планах и опыте, перейдите на блог сообщества Genodians.org.

  :Genodians.org:

    [https://genodians.org]